﻿using ArtMath.Core.Helper;
using System;
using System.Collections.Generic;
using System.Drawing;

namespace ArtMath.Core.Data
{
    [Serializable]
    public struct RectangleD
    {
        public static readonly RectangleD Empty = new RectangleD(0, 0, 0, 0);
        public double Left { get; set; }
        public double Top { get; set; }
        public double Width { get; set; }
        public double Height { get; set; }
        public double Right
        {
            get { return Left + Width; }
        }
        public double Bottom
        {
            get { return Top + Height; }
        }
        public PointD Location
        {
            get { return new PointD(Left, Top); }
            set
            {
                Left = value.X;
                Top = value.Y;
            }
        }
        public PointD BottomLeft
        {
            get { return new PointD(Left, Bottom); }
        }
        public PointD BottomRight
        {
            get { return new PointD(Right, Bottom); }
        }
        public PointD TopRight
        {
            get { return new PointD(Right, Top); }
        }
        public PointD TopCenter
        {
            get { return new PointD(Left + Width / 2, Top); }
        }
        public PointD MiddleLeft
        {
            get { return new PointD(Left, Top + Height / 2); }
        }
        public PointD MiddleRight
        {
            get { return new PointD(Right, Top + Height / 2); }
        }
        public PointD BottomCenter
        {
            get { return new PointD(Left + Width / 2, Bottom); }
        }
        public PointD Center
        {
            get { return new PointD(Left + Width / 2, Top + Height / 2); }
        }
        public SizeD Size
        {
            get { return new SizeD(Width, Height); }
            set
            {
                Width = value.Width;
                Height = value.Height;
            }
        }
        public bool IsMassPoint
        {
            get { return Width == 0 || Height == 0; }
        }
        public bool IsDrawable
        {
            get { return Left.IsNumber() && Top.IsNumber() && Width.IsNumber() && Height.IsNumber() && !IsMassPoint; }
        }
        public RectangleD(PointD location, SizeD size)
        {
            Left = location.X;
            Top = location.Y;
            Width = size.Width;
            Height = size.Height;
        }
        public RectangleD(double left, double top, double width, double height)
        {
            Left = left;
            Top = top;
            Width = width;
            Height = height;
        }
        public PointD[] GetVertices()
        {
            return new PointD[] { Location, BottomLeft, BottomRight, TopRight };
        }
        public RectangleF ToRectangleF()
        {
            return new RectangleF((float)Left, (float)Top, (float)Width, (float)Height);
        }
        public RectangleD ToStandardRectangle()
        {
            double l, t, w, h;
            if (Width < 0)
            {
                l = Left + Width;
                w = -Width;
            }
            else
            {
                l = Left;
                w = Width;
            }
            if (Height < 0)
            {
                t = Top + Height;
                h = -Height;
            }
            else
            {
                t = Top;
                h = Height;
            }
            return new RectangleD(l, t, w, h);
        }
        public Region ToRegion()
        {
            return new Region(ToRectangleF());
        }
        public bool Contains(PointD pt)
        {
            return Contains(pt.X, pt.Y);
        }
        public bool Contains(double x, double y)
        {
            return x >= Left && x <= Right && y >= Top && y <= Bottom;
        }
        public bool IntersectLineSegment(PointD segpt1, PointD segpt2)
        {
            return CurveIntersection.CountLineSegmentsIntersections(segpt1, segpt2, Location, BottomLeft) == 1 ||
                CurveIntersection.CountLineSegmentsIntersections(segpt1, segpt2, BottomLeft, BottomRight) == 1 ||
                CurveIntersection.CountLineSegmentsIntersections(segpt1, segpt2, BottomRight, TopRight) == 1 ||
                CurveIntersection.CountLineSegmentsIntersections(segpt1, segpt2, TopRight, Location) == 1 ||
                Contains(segpt1) && Contains(segpt2);
        }
        public bool IntersectRay(PointD src, double angle)
        {
            return CurveIntersection.CountLineSegmentRayIntersections(Location, BottomLeft, src, angle) == 1 ||
                CurveIntersection.CountLineSegmentRayIntersections(BottomLeft, BottomRight, src, angle) == 1 ||
                CurveIntersection.CountLineSegmentRayIntersections(BottomRight, TopRight, src, angle) == 1 ||
                CurveIntersection.CountLineSegmentRayIntersections(TopRight, Location, src, angle) == 1;
        }
        public bool IntersectStraightLine(PointD pt, double k)
        {
            return CurveIntersection.CountLineSegmentStraightLineIntersections(Location, BottomLeft, pt, k) == 1 ||
                CurveIntersection.CountLineSegmentStraightLineIntersections(BottomLeft, BottomRight, pt, k) == 1 ||
                CurveIntersection.CountLineSegmentStraightLineIntersections(BottomRight, TopRight, pt, k) == 1 ||
                CurveIntersection.CountLineSegmentStraightLineIntersections(TopRight, Location, pt, k) == 1;
        }
        //public bool IntersectCurves(IEnumerable<Curve> curs)
        //{
        //    foreach (Curve item in curs)
        //    {
        //        if (IntersectCurve(item))
        //        {
        //            return true;
        //        }
        //    }
        //    return false;
        //}
        public bool IntersectCurves<T>(IEnumerable<T> pts) where T : IEnumerable<PointD>
        {
            foreach (T item in pts)
            {
                if (IntersectCurve(item))
                {
                    return true;
                }
            }
            return false;
        }
        public bool IntersectCurve(IEnumerable<PointD> pts)
        {
            foreach (PointD item in pts)
            {
                if (Contains(item))
                {
                    return true;
                }
            }
            return false;
        }
        public bool IntersectRectangle(RectangleD rect)
        {
            PointD ct1 = Center, ct2 = rect.Center;
            return Math.Abs(ct1.X - ct2.X) < (Math.Abs(Width) + Math.Abs(rect.Width)) / 2 && 
                Math.Abs(ct1.Y - ct2.Y) < (Math.Abs(Height) + Math.Abs(rect.Height)) / 2;
        }
        //public bool IntersectArc(PointD center, double radius, double start, double sweep)
        //{
        //    return CurveIntersection.LineSegmentArcIntersection(Location, LeftBottom, center, radius, start, sweep)?.Length > 0 ||
        //        CurveIntersection.LineSegmentArcIntersection(LeftBottom, RightBottom, center, radius, start, sweep)?.Length > 0 ||
        //        CurveIntersection.LineSegmentArcIntersection(RightBottom, RightTop, center, radius, start, sweep)?.Length > 0 ||
        //        CurveIntersection.LineSegmentArcIntersection(RightTop, Location, center, radius, start, sweep)?.Length > 0;
        //}
        public static bool operator ==(RectangleD l, RectangleD r)
        {
            return l.Left == r.Left && l.Top == r.Top && l.Width == r.Width && l.Height == r.Height;
        }
        public static bool operator !=(RectangleD l, RectangleD r)
        {
            return !(l == r);
        }
        public override bool Equals(object obj)
        {
            if (obj is RectangleD)
            {
                RectangleD rect = (RectangleD)obj;
                return this == rect;
            }
            return false;
        }
        public override int GetHashCode()
        {
            long bits = BitConverter.DoubleToInt64Bits(Left) + 
                BitConverter.DoubleToInt64Bits(Top) * 37 + 
                BitConverter.DoubleToInt64Bits(Width) * 43 + 
                BitConverter.DoubleToInt64Bits(Height) * 47;
            return ((int)bits) ^ ((int)(bits >> 32));
        }
        public override string ToString()
        {
            return $"{Left}, {Top}, {Width}, {Height}";
        }
    }
}
